package fuente;

import estructuras.ANB;
import estructuras.ANBNode;
import estructuras.SLL;
import estructuras.SLLNode;

/**
 * Clase que representa al usuario que interactuará con el programa, se crea una
 * nueva instancia para cada nuevo proecto o diseño que se valla a crear,
 * Contiene los métodos por medio de los cuales se efectúan las tareas
 * que el usuario quiere ejecutar en el progra y que comunican la 
 * parte lógica del programa con la interfaz del usuario, 
 * así que funciona como patrón de diseño FACADE.
 * @author Alexandre
 * @author Rodrigo
 * @author Gabriel
 * @author Jeremy
 */

public class Usuario {
    
    private String _nombre;//Nombre del usuario o el nuevo diseño
    private String _descripcion;//Descripción breve del nuevo diseño creado
    private int _entradas;//Cantidad de entradas que se desea para el nuevo diseño
    private int _salidas;//Cantidad de salidas que se desea para el nuevo diseño
    
    private int _contadorCompuertasAND;//Mantiene cuenta de la cantidad de compuertasAND creadas, para añadir un identificador al nombre gráficamente
    private int _contadorCompuertasNAND;//Mantiene cuenta de la cantidad de compuertasNAND creadas, para añadir un identificador al nombre gráficamente     
    private int _contadorCompuertasOR;//Mantiene cuenta de la cantidad de compuertasOR creadas, para añadir un identificador al nombre gráficamente
    private int _contadorCompuertasNOR;//Mantiene cuenta de la cantidad de compuertasNOR creadas, para añadir un identificador al nombre gráficamente
    private int _contadorCompuertasNOT;//Mantiene cuenta de la cantidad de compuertasNOT creadas, para añadir un identificador al nombre gráficamente
    private int _contadorCompuertasXOR;//Mantiene cuenta de la cantidad de compuertasXOR creadas, para añadir un identificador al nombre gráficamente
    private int _contadorCompuertasXNOR;//Mantiene cuenta de la cantidad de compuertasXNOR creadas, para añadir un identificador al nombre gráficamente
    
    private SLL _listaArboles;//Almacena los árboles creados de acuerdo a la cantidad de salidas
    private SLL _listaCompuertas;//Almacena las compuertas lógicas creadas (Nodos de árbol) para no perder referencia a ellas antes de crear sus enlaces
    private SLL _listaEntradas;//Almacena las entradas del diseño(Nodos de árbol)

    
    /**
     * Constructor, Inicializa los atributos:
     * _nombre y _descripción como strings vacíos
     * _entradas en 1
     * _salidas en 1
     * _Compuertas creadas en 1 (para comenzar la numeración de las etiquetas en 1)
     * e instancia las nuevas listas simples.
     */
    public Usuario () {
        _nombre = _descripcion= "";
        _entradas = _salidas = 1;
        
        _contadorCompuertasAND = _contadorCompuertasNAND = _contadorCompuertasOR = 
        _contadorCompuertasNOR = _contadorCompuertasNOT = _contadorCompuertasXOR = 
        _contadorCompuertasXNOR = 1;
        
        _listaArboles = new SLL();
        _listaCompuertas = new SLL();
        _listaEntradas = new SLL();
    }
    
    ////////////////////////////Getters y setters///////////////////////////////
    /**
     * Obtiene el contador para compuertas AND
     * @return Cantidad de compuertas AND creadas
     */
    public int getContadorCompuertasAND() {
        return _contadorCompuertasAND;
    }

    /**
     * Establece el contador para compuertas AND
     * @param pContadorCompuertasAND Cantidad a establecer
     */
    public void setContadorCompuertasAND(int pContadorCompuertasAND) {
        this._contadorCompuertasAND = pContadorCompuertasAND;
    }

    /**
     * Obtiene el contador para compuertas NAND
     * @return Cantidad de compuertas NAND creadas
     */
    public int getContadorCompuertasNAND() {
        return _contadorCompuertasNAND;
    }

    /**
     * Establece el contador para compuertas NAND
     * @param pContadorCompuertasNAND Cantidad para establecer
     */
    public void setContadorCompuertasNAND(int pContadorCompuertasNAND) {
        this._contadorCompuertasNAND = pContadorCompuertasNAND;
    }
    
    /**
     * Obtiene el contador para compuertas OR
     * @return Cantidad de compuertas OR creadas
     */
    public int getContadorCompuertasOR() {
        return _contadorCompuertasOR;
    }

    /**
     * Establece el contador para compuertas OR
     * @param pContadorCompuertasOR Cantidad para establecer
     */
    public void setContadorCompuertasOR(int pContadorCompuertasOR) {
        this._contadorCompuertasOR = pContadorCompuertasOR;
    }

    /**
     * Establece el contador para compuertas NOR
     * @return Cantidad de compuertas NOR credas
     */
    public int getContadorCompuertasNOR() {
        return _contadorCompuertasNOR;
    }

    /**
     * Establece el contador para compuertas NOR
     * @param pContadorCompuertasNOR Cantidad pars establecer
     */
    public void setContadorCompuertasNOR(int pContadorCompuertasNOR) {
        this._contadorCompuertasNOR = pContadorCompuertasNOR;
    }

    /**
     * Obtiene el contador para compuertas NOT
     * @return Cantidad de compuertas NOT creadas
     */
    public int getContadorCompuertasNOT() {
        return _contadorCompuertasNOT;
    }

    /**
     * Establece el contador para compuertas NOT
     * @param pContadorCompuertasNOT Cantidad para establecer
     */
    public void setContadorCompuertasNOT(int pContadorCompuertasNOT) {
        this._contadorCompuertasNOT = pContadorCompuertasNOT;
    }

    /**
     * Obtiene el contador para compuertas XOR
     * @return Cantidad de compuertasXOR creadas
     */
    public int getContadorCompuertasXOR() {
        return _contadorCompuertasXOR;
    }

    /**
     * Establece el contador para compuertas XOR
     * @param pContadorCompuertasXOR Cantidad para establecer
     */
    public void setContadorCompuertasXOR(int pContadorCompuertasXOR) {
        this._contadorCompuertasXOR = pContadorCompuertasXOR;
    }

    /**
     * Obtiene el contador para compuertas XNOR
     * @return cantidad de compuertasXNOR creadas
     */
    public int getContadorCompuertasXNOR() {
        return _contadorCompuertasXNOR;
    }

    /**
     * Establece el contador para compuertas XNOR
     * @param pContadorCompuertasXNOR Cantidad para establecer
     */
    public void setContadorCompuertasXNOR(int pContadorCompuertasXNOR) {
        this._contadorCompuertasXNOR = pContadorCompuertasXNOR;
    }
    
    /**
     * Obtiene el nombre del diseño
     * @return Nombre del diseño
     */
    public String getNombre() {
        return _nombre;
    }

    /**
     * Establece el nombre del diseño
     * @param pNombre Nombre del diseño
     */
    public void setNombre(String pNombre) {
        this._nombre = pNombre;
    }

    /**
     * Obtiene la descripción del diseño hecha por el usuario al iniciar
     * el nuevo diseño
     * @return Descripción del diseño
     */
    public String getDescripcion() {
        return _descripcion;
    }

    /**
     * Establece la descripción del diseño hecha por el usuario
     * @param pDescripcion Descripción del diseño
     */
    public void setDescripcion(String pDescripcion) {
        this._descripcion = pDescripcion;
    }

    /**
     * Obtiene la cantidad de entradas del diseño definidas por el usuario
     * @return Cantidad de entradas del diseño
     */
    public int getEntradas() {
        return _entradas;
    }

    /**
     * Establece la cantidad de entradas que va a tener el diseño
     * @param pEntradas Cantidad de entradas
     */
    public void setEntradas(int pEntradas) {
        this._entradas = pEntradas;
    }

    /**
     * Obtiene la cantidad de salidas que va a tener el diseño
     * @return Cantidad de salidas
     */
    public int getSalidas() {
        return _salidas;
    }

    /**
     * Establece la cantidad de salidas que tiene el diseño
     * @param pSalidas Cantidad de salidas
     */
    public void setSalidas(int pSalidas) {
        this._salidas = pSalidas;
    }
    
    /**
     * Obtiene la lista de árboles que se han creado por el usuario, las raíces 
     * de esos árboles son las salidas del diseño lógico
     * @return Lista de árboles
     */
    public SLL getListaArboles() {
        return _listaArboles;
    }

    /**
     * Establece una lista nueva de árboles que representan la interconexión
     * del diseño lógico
     * @param pListaArboles Lista de árboles
     */
    public void setListaArboles(SLL pListaArboles) {
        this._listaArboles = pListaArboles;
    }

    /**
     * Obtiene la lista de compuertas lógicas creadas, cada nodo contiene
     * una compuerta lógica que se añade al crear una nueva en el editor
     * @return Lista de compuertas lógicas creadas por el usuario
     */
    public SLL getListaCompuertas() {
        return _listaCompuertas;
    }

    /**
     * Etablece una nueva lista de compuertas lógicas
     * @param pListaCompuertas Lista de compuertas creadas
     */
    public void setListaCompuertas(SLL pListaCompuertas) {
        this._listaCompuertas = pListaCompuertas;
    }

    /**
     * Obtiene la lista de entradas del diseño lógico, en la lista cada nodo
     * contendrá como elemento una entrada del diseño, la cual, tendrá
     * sólo una lista de padres, osea, hacia qué compuertas va dirigida esa
     * entrada, y además su nombre de entrada (En, donde n es el número de entrada)
     * @return Lista de entradas
     */
    public SLL getListaEntradas() {
        return _listaEntradas;
    }

    /**
     * Establece una lista nueva de entradas del diseño lógico, en la lista cada nodo
     * contendrá como elemento una entrada del diseño, la cual, tendrá
     * sólo una lista de padres, osea, hacia qué compuertas va dirigida esa
     * entrada, y además su nombre de entrada (En, donde n es el número de entrada)
     * @param pListaEntradas Lista de entradas
     */
    public void setListaEntradas(SLL pListaEntradas) {
        this._listaEntradas = pListaEntradas;
    }
    ////////////////////////////////////////////////////////////////////////////
    
    
    /**
     * Crea una nueva compuerta lógica (Nodo de árbol) y la añade a la lista respectiva
     * de compuertas creadas (_compuertasCreadas).
     * @param pNombreCompuerta Nombre de la compuerta para crear
     */
    public void crearCompuerta (String pNombreCompuerta, int pX, int pY) {
        int cont = 0;
        boolean flag = true;
        switch (pNombreCompuerta) {
            case "VCC":
                flag = false;
                break;
            case "GND":
                flag = false;
                break;
            case "AND":
                cont = _contadorCompuertasAND;
                _contadorCompuertasAND++;
                break;
            case "NAND":
                cont = _contadorCompuertasNAND;
                _contadorCompuertasNAND++;
                break;
            case "OR":
                cont = _contadorCompuertasOR;
                _contadorCompuertasOR++;
                break;
            case "NOR":
                cont = _contadorCompuertasNOR;
                _contadorCompuertasNOR++;
                break;
            case "NOT":
                cont = _contadorCompuertasNOT;
                _contadorCompuertasNOT++;
                break;
            case "XOR":
                cont = _contadorCompuertasXOR;
                _contadorCompuertasXOR++;
                break;
            case "XNOR":
                cont = _contadorCompuertasXNOR;
                _contadorCompuertasXNOR++;
                break;
        }
        ANBNode comp = new ANBNode(pNombreCompuerta, cont, flag, pX, pY);
        _listaCompuertas.meterFinal(comp);
        
        String s = comp.getCompuertaGraf()+"----"+comp.getCompuertaName();
        System.out.println(s);
    }
    
    /**
     * Crea una conexión entre dos compuertas lógicas
     * @param pCompuertaFuente Nombre de la compuerta de la cual sale el dato
     * @param pCompuertaDestino Nombre de la compuerta hacia la cual va el dato
     * @param pSalida Dice si la pCompuertaDestino es un componente salida del diseño total, si es true crea un nuevo arbol y lo añade a _listaArboles
     */
    public void crearConexión (String pCompuertaFuente, String pCompuertaDestino, boolean pSalida) {
        ANBNode nF = null;
        ANBNode nD = null;
        for (SLLNode n = _listaCompuertas.getHead(); n != null; n = n.getNext()) {
            ANBNode nComp = (ANBNode) n.getElemento();
            if (nComp.getCompuertaGraf().equals(pCompuertaFuente)) {
                nF = nComp;
                break;
            }
        }
        
        if (nF == null) {
            System.out.println("No se encontro nodo fuente");
            return;
        }
        
        for (SLLNode n = _listaCompuertas.getHead(); n != null; n = n.getNext()) {
            ANBNode nComp = (ANBNode) n.getElemento();
            if (nComp.getCompuertaGraf().equals(pCompuertaDestino)) {
                nD = nComp;
                break;
            }
        }
        
        if (nD == null) {
            System.out.println("No se encontro nodo destino");
            return;
        }
        
        nF.setPadre(nD);
        nD.agregarHijo(nF);
        
        if (pSalida) {
            ANB arbol = new ANB(nD);
            _listaArboles.meterFinal(arbol);
        }
    }
    
    /**
     * Encuentra una compuerta de la lista a partir de su nombre y lo retorna
     * @param pNombre Nombre grafico de la compuerta
     * @return Nodo encontrado
     */
    public ANBNode encontrarCompuerta (String pNombre) {
        ANBNode Salida=null;
        for (SLLNode n = _listaCompuertas.getHead(); n != null; n = n.getNext()) {
             ANBNode nComp = (ANBNode) n.getElemento();
             if (nComp.getCompuertaGraf().equals(pNombre)) {
                 Salida=nComp;  
            }
         }
        return Salida;
    }
    
    /**
     * Encuentra los nodos de árboles (ANBNode; compuertas lógicas) y crea una lista de ellos
     * a partir de una lista de sus nombres (Strings).
     * @param pListaNombresCompuertas Lista de nombres de compuertas para encontrar y almacenar en una lista simple
     * @return Lista simple de los nodos de árboles o compuertas lógicas encontradas
     */
    private SLL encontrarCompuertas (SLL pListaNombresCompuertas) {
        SLL listaCompuertasDeEntradas = new SLL();
        for (SLLNode nN = pListaNombresCompuertas.getHead(); nN != null; nN = nN.getNext()) {
            String nombre = (String) nN.getElemento();
            
            for (SLLNode nC = pListaNombresCompuertas.getHead(); nC != null; nC = nC.getNext()) {
                ANBNode nComp = (ANBNode) nC.getElemento();
                String nombreComp = nComp.getCompuertaGraf();
                
                if (nombreComp.equals(nombre)) {
                    listaCompuertasDeEntradas.meterFinal(nComp);
                }
            }
        }
        return listaCompuertasDeEntradas;
    }
    
    /**
     * Crea una nueva entrada referenciada ya sea a VCC o a GND
     * estos definidos desde el inicio de la ejecucion del programa
     * @param pPadre Nombre de la compuerta destino
     * @param pEntrada true si es VCC, false para GND
     */
    public void crearEntrada (ANBNode pPadre, boolean pEntrada, int pX, int pY, boolean pCheckBox) {
        if (pEntrada) {
            ANBNode VCC = (ANBNode)_listaCompuertas.obtenerPorPosicion(1);
            VCC.getHijos().meterFinal(pPadre);
            
            pPadre.getHijos().meterFinal(VCC);
            
            ANB arbol = new ANB(pPadre);
            
            if (pCheckBox) {
                _listaArboles.meterFinal(arbol);
            }
        }
        else {
            ANBNode GND = (ANBNode)_listaCompuertas.obtenerPorPosicion(2);
            GND.getHijos().meterFinal(pPadre);
            
            pPadre.getHijos().meterFinal(GND);
            
            ANB arbol = new ANB(pPadre);
            
            if (pCheckBox) {
                _listaArboles.meterFinal(arbol);
            }
        }
    }
    
    /**
     * Simula una salida del diseño de acuerdo a una/s entrada/s y retorna 
     * una lista de salidas del diseño.
     * @return Lista de salidas
     */
    public SLL simular() {
        SLL listaSalidas = new SLL();
        
        for (SLLNode n = _listaArboles.getHead(); n != null; n = n.getNext()) {
            ANB arbol = (ANB) n.getElemento();
            System.out.println(arbol.getRaiz().getCompuertaGraf());
            listaSalidas.meterFinal(arbol.obtenerSalida(arbol.getRaiz()));
        }
        return listaSalidas;
    }
    
    /**
     * Busa una compuerta en el arbol del diseño y dice si la encontro o no
     * @param pCompuerta Compuerta a buscar
     * @return true si la encuentra, false de lo conptrario
     */
    public boolean isEnListaSalidas (ANBNode pCompuerta) {
        for (SLLNode n = _listaArboles.getHead(); n != null; n = n.getNext()) {
            ANB arb = (ANB)n.getElemento();
            ANBNode root = arb.getRaiz();
            
            if (pCompuerta.getCompuertaGraf().equals(root.getCompuertaGraf())) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Ejecuta la accion de guardar los datos del diseño en un XML,
     * llama al objeto GuardarArchivoXML.
     */
    public void guardar() {
        SLL salidasL = simular();
        String salidasStr = "";
        for (SLLNode n = salidasL.getHead(); n != null; n = n.getNext()) {
            int valor = (int) n.getElemento();
            
            salidasStr = salidasStr+valor+", ";
        }
        String entradas = String.valueOf(_entradas);
        
        ANBNode VCCNode = (ANBNode) _listaCompuertas.getHead().getElemento();
        int papasVCC = VCCNode.getHijos().getLength();
        String VCC = String.valueOf(papasVCC);
        
        ANBNode GNDNode = (ANBNode) _listaCompuertas.getHead().getNext().getElemento();
        int papasGND = GNDNode.getHijos().getLength();
        String GND = String.valueOf(papasGND);
        
        GuardarArchivo.GuardarArchivo(_nombre, _descripcion, entradas, VCC, GND, salidasStr);
    }
}
